package base.stream;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;

// 字符缓冲流

public class BufferedReaderAndWriteDemo {
    public static void main(String[] args) throws IOException {

        // demo
//        readerAndWriter();

        // 使用字节流读取网页
//        convertTest1();

        // 使用字符缓冲流读取网页
//        convertTest2();

        // 可重复使用
        reuseableBufferedReader();
    }

    public static void readerAndWriter() throws FileNotFoundException {

        try (
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in), 1);
                BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(System.out))

        ) {

            String strLine = "";
            while (!"exit".equals(strLine)) {
                // 读取一行
                strLine =  bufferedReader.readLine();
                bufferedWriter.write(strLine);
                bufferedWriter.newLine();
                bufferedWriter.flush();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }


    }

    private static void convertTest1() throws IOException {
        // 字节流读取
        InputStream is = new URL("http://www.baidu.com/").openStream();
        int data = 0;
        while (data != -1) {
            data = is.read();
            System.out.println((char)data);
        }

    }

    // Use Buffered
    private static void convertTest2()  {
        // 字符流读取
        try(
            BufferedReader reader = new BufferedReader(
                    new InputStreamReader(
                            new URL("http://www.baidu.com/").openStream(), Charset.forName("UTF-8")
                    )
            )
        ) {
            String msg;
            while (( msg = reader.readLine() )!= null ) {
                System.out.println(msg);
            }

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public static void reuseableBufferedReader() throws IOException {
        // 1. Create a ReusableBufferedReader
        ReuseableBufferReader reuseable = new ReuseableBufferReader(new char[1024 * 1024]);

        // 2. Set Source
        FileReader reader = new FileReader("C:\\Temp\\test\\test.txt");
        reuseable.setSource(reader);

        // 3. Reusing
//        reuseable.read();
        reuseable.close();

        reuseable.setSource(new FileReader("C:\\Temp\\test\\test2.txt"));
//        reuseable.read();
        reuseable.close();

    }

}

class ReuseableBufferReader extends Reader {
    private char[]      buffer              = null;
    private int         writeIndex          = 0;
    private int         readIndex           = 0;
    private boolean     endOfReaderReached  = false;

    private Reader      source              = null;

    public ReuseableBufferReader( char[] buffer) {
        this.buffer = buffer;
    }

    public ReuseableBufferReader setSource(Reader reader) {
        this.source             = reader;
        this.writeIndex         = 0;
        this.readIndex          = 0;
        this.endOfReaderReached = false;

        return this;
    }

    @Override
    public int read() throws IOException {
        if (endOfReaderReached) {
            return -1;
        }

        if (readIndex == writeIndex) {
            if (writeIndex == buffer.length) {
                this.writeIndex = 0;
                this.readIndex = 0;
            }
            // data should be read into buffer;
            int bytesRead = readCharsIntoBuffer();

            while (bytesRead == 0) {
                // continue until you actually get some bytes;
                bytesRead = readCharsIntoBuffer();
            }
            // if no more data could be read in, return -1;
            if (bytesRead == -1) {
                return -1;
            }

        }

        return 65535 & this.buffer[readIndex++];
    }

    private int readCharsIntoBuffer() throws IOException {
        int charsRead = this.source.read(this.buffer, this.writeIndex, this.buffer.length - this.writeIndex);
        writeIndex += charsRead;
        return charsRead;
    }


    @Override
    public int read(char[] cbuf, int off, int len) throws IOException {
        int charsRead = 0;
        int data = 0;

        while (data != -1 && charsRead < len) {
            data = read();
            if (data == -1) {
                endOfReaderReached = true;
                if (charsRead == 0) {
                    return -1;
                }
                return charsRead;
            }
            cbuf[off + charsRead] = (char) (65535 & data);
            charsRead++;
        }

        return charsRead;
    }

    @Override
    public void close() throws IOException {
        this.source.close();
    }
}